#include "stdafx.h"
#include "AlignmentsQ.h"

const unsigned int CAlignmentsQ::NULL_RECORD = std::numeric_limits<unsigned int>::max();
const unsigned short CAlignmentsQ::NULL_EDIT_DIS = std::numeric_limits<unsigned short>::max();

CAlignmentsQ::CAlignmentsQ(unsigned int iMaxCapacity)
{
    this->initialization(iMaxCapacity);
}

CAlignmentsQ::CAlignmentsQ(char cFlag_of_Queue_All_Best_One, unsigned int iMaxCapacity)
{
    this->initialization(iMaxCapacity);
    this->cFlag_of_Queue_All_Best_One = cFlag_of_Queue_All_Best_One;
}

CAlignmentsQ::~CAlignmentsQ(void)
{

}

void CAlignmentsQ::setQueue_All_Best_OneFlag(char cFlag_of_Queue_All_Best_One)
{   //Simply a public function used to set the flag option to queue the best set or all or one alignment
    this->cFlag_of_Queue_All_Best_One = cFlag_of_Queue_All_Best_One;
}

char CAlignmentsQ::returnQueue_All_Best_OneFlag()
{
    return(this->cFlag_of_Queue_All_Best_One);
}

/*
Originally pushHits has signiture pushHits(unsigned int startindex, unsigned short diff) only
This is because if a read mapped to a ref and its complement in the exactly the same position
Ex: ACGTACGT, the queue only store one copy. This is changed because for the paired end,
the dirrection of both dirrection should be stored.
*/

unsigned int CAlignmentsQ::pushHits(unsigned int startindex, unsigned short diff, bool forward)
{
    // Only push in when the new alignment is better or if we choose to collect all alignment
    // Or if we choose to collect all best alignment and the coming alignment is as good as the best one
    if (diff <= this->MinDiff || this->cFlag_of_Queue_All_Best_One == 'A') {
        //(1) linear search and see if there is duplicate record
        // TODO this may be the buttleneck of speed.
        unsigned int checkQStart = forward ? 0 : this->ForwardAlignmentLoad;
        unsigned int checkQEnd = forward ? this->ForwardAlignmentLoad : this->load;
        unsigned int i;
        for (i = checkQStart; i < checkQEnd && i < this->iMaxCapacity; i++) {
            //Use + instead of - because unsigned int need to be carefully deal with.
            if (this->aiHitIndex[i] == startindex) {
                return(load);// This alignment is in the record
            }
        }
        //(2) see if the new alignment is better
        if (diff < this->MinDiff) {
            this->MinDiff = diff;//Update MinDiff
            if (this->cFlag_of_Queue_All_Best_One == 'B') {
                // If only the best set of alignments are collected
                // and the coming alignment is better, throw away all previous record
                this->load = 0; //NOT this->clearHits();
                this->ForwardAlignmentLoad = 0;
            } // Definition of non-ambiguous is having a unique best mapping.
            this->AmbiguousFlag = false;
        } else if (diff == this->MinDiff) {
            this->AmbiguousFlag = true; // This is put at the end to avoid duplicate record.
        }
        //(3) record the alignment in the queue
        if (this->load < this->iMaxCapacity - 1) {
            this->aiHitIndex[this->load] = startindex;
            this->asdiff[this->load] = diff;
            this->load++;
        } else { // if the buffer is overflow.
            // cout << "Alignment Queue overflow" << endl;
        }
    }
    return(load);
}

int CAlignmentsQ::initialization(unsigned int MAX_Q_CAPACITY)
{
    this->iMaxCapacity = MAX_Q_CAPACITY;
    this->load = 0;
    this->clearHits();
    // The default setting is to queue the best set (could be more than one) alignment
    this->cFlag_of_Queue_All_Best_One = 'B';
    for (unsigned int i = 0; i < this->iMaxCapacity; i++) {
        this->aiHitIndex[i] = NULL_RECORD;
        this->asdiff[i] = NULL_EDIT_DIS;
    }
    this->qualityScores = NULL;
    this->readID = 0;
    this->tag[0] = '\0';
    return 0;

}

int CAlignmentsQ::clearHits()
{
    for (unsigned int i = 0; i < min(this->load, this->iMaxCapacity); i++) {
        this->aiHitIndex[i] = NULL_RECORD;
        this->asdiff[i] = NULL_EDIT_DIS;
    }
    /* Marked this to increase speed */
    this->load = 0;
    this->ForwardAlignmentLoad = 0;
    this->MinDiff = MAX_READ_LENGTH;
    this->AmbiguousFlag = false;
    this->reverseIsBetter = false;
    return(0);
}

int CAlignmentsQ::sortHitsByLocation()
{
    // Sort according to distance
    vector< pair <int, int> > v;
    for (unsigned int i = 0; i < this->ForwardAlignmentLoad; i++) {
        v.push_back(pair<unsigned int, unsigned short>(this->aiHitIndex[i], this->asdiff[i]));
    }
    std::sort(v.begin(), v.end());
    for (unsigned int i = 0; i < this->ForwardAlignmentLoad; i++) {
        this->aiHitIndex[i] = v.at(i).first;
        this->asdiff[i] = (unsigned short)(v.at(i).second);
    }

    vector< pair <int, int> > w;
    for (unsigned int i = this->ForwardAlignmentLoad; i < this->load; i++) {
        w.push_back(pair<unsigned int, unsigned short>(this->aiHitIndex[i], this->asdiff[i]));
    }
    std::sort(w.begin(), w.end());
    for (unsigned int i = this->ForwardAlignmentLoad; i < this->load; i++) {
        this->aiHitIndex[i] = w.at(i).first;
        this->asdiff[i] = (unsigned short)w.at(i).second;
    }
    return(0);
}

int CAlignmentsQ::filterAlignments(unsigned int mismatchThreshold, bool bKeepAllAlignmentsInThreshold)
{
    if (this->MinDiff > mismatchThreshold) {
        this->clearHits();
    } else {
        unsigned int i, j; // move record from i to j
        for (i = 0, j = 0; i < this->load; i++) {
            if (i == this->ForwardAlignmentLoad) {
                this->ForwardAlignmentLoad = j;
            }
            if ((unsigned int)this->asdiff[i] == this->MinDiff || (bKeepAllAlignmentsInThreshold\
                    && (unsigned int) this->asdiff[i] <= mismatchThreshold)) {
                this->aiHitIndex[j] = this->aiHitIndex[i];
                this->asdiff[j] = this->asdiff[i];
                j++;
            }
        }
        this->load = j;
        if (this->load <= 1) {
            this->AmbiguousFlag = false;
        } else {
            this->AmbiguousFlag = true;
        }
    }
    return(this->load);
}

//Simply to a linear search in the queue to find the best alignment and return the genome InDex
unsigned int CAlignmentsQ::topHitsinList(void)
{
    unsigned short mindiff = NULL_EDIT_DIS;//Check it again
    unsigned int bestHitsIndex = 0;
    unsigned int i;
    for (i = 0; i < this->load; i++) {
        if (mindiff > this->asdiff[i]) {
            mindiff = this->asdiff[i];
            bestHitsIndex = i;
            this->AmbiguousFlag = false;
        } else if (mindiff == this->asdiff[i])
            this->AmbiguousFlag = true;
    }
    if (mindiff < MAXTOLERATSUBMIS) {
        this->reverseIsBetter = (bestHitsIndex >= this->ForwardAlignmentLoad);
        return(this->aiHitIndex[bestHitsIndex]);
    } else
        return(NULL_RECORD);//(Confuse between bad kmer and not found)
}

